import os
import socket
import threading
import time
import re

# 存储所有连接的客户端
clients = {}
# 全局锁
clients_lock = threading.Lock()

# 发送消息（心跳消息、服务端消息、转发客户端消息）
def broadcast_message(message, sender_name):
    with clients_lock:  # 加锁创建副本
        send_list = [(n, c['conn']) for n, c in clients.items() if n != sender_name] # 排除最初发送消息的客户端
    broken_conns = [] # 记录失效连接
    for name, conn in send_list:
        try:
            conn.sendall(message)
        except (BrokenPipeError, ConnectionResetError):
            print(f'【system】客户端 {name} 断开连接')
            broken_conns.append(conn)
    # 统一清理失效连接
    for conn in broken_conns:
        remove_client(conn)
# 已注册用户断开连接（未注册直接调close_conn）
def remove_client(conn):
    with clients_lock:  # 加锁操作字典
        for name, client in list(clients.items()):
            if client['conn'] == conn:
                del clients[name]
                close_conn(conn)
                break

def close_conn(conn):
    try:
        conn.shutdown(socket.SHUT_RDWR) # 通知对端关闭连接，确保所有数据都已被发送并且被对方接收
        # 清空接收缓冲区
        while True:
            data = conn.recv(1024)
            if not data:
                break
    except OSError:
        pass # 如果连接已经断开或其他原因无法调用 shutdown，则忽略异常
    finally:
        try:
            conn.close()
        except OSError:
            pass

# 接受客户端消息的线程（包括连接注册和自定义消息）
def handle_client(conn, addr):
    name = None
    print(f'\n【system】地址 {addr} 已连接，待注册用户名')
    try:
        conn.settimeout(10)  # 10秒内不注册则超时
        data = conn.recv(1024).decode().strip()
        match = re.match(r'-name\s+(\S+)', data)
        if not match:
            conn.sendall('【Error】客户端注册失败\n'.encode('gbk'))
            print(f'【Error】客户端注册失败')
            return

        # 接收客户端的名称
        name = match.group(1)
        with clients_lock:
            if name in clients:
                conn.sendall('【Error】该名称已被使用！\n'.encode('gbk'))
                print(f'【Error】客户端尝试注册用户名 {name} ，但该名称已被使用！')
                close_conn(conn)
                return
            clients[name] = {'conn': conn, 'addr': addr} # 记录客户端名称及其对应连接地址
        print(f'【system】地址 {addr} 注册用户名为 {name}', end='\n【system】请输入命令或要发送的消息 (输入 \'help\' 查询指令): ')
        conn.settimeout(None)  # 取消超时限制

        while True:
            # 接收自定义消息
            message = conn.recv(1024)
            if not message:
                break
            try:
                message_str = message.decode('utf-8').strip() # 默认使用 utf-8 编码解码
            except UnicodeDecodeError:
                try:
                    message_str = message.decode('gbk').strip() # 尝试使用 GBK 编码解码
                except UnicodeDecodeError:
                    print(f'【Error】消息编码错误', flush=True)
                    conn.sendall('【Error】消息编码错误\n'.encode('gbk'))
                    return

            print(f'\n【{name}】{message_str}', end='\n【system】请输入命令或要发送的消息 (输入 \'help\' 查询指令): ')
            # print(f'\n【{name}】{message_str}')
            message = f'【{name}】{message_str}\n'.encode('gbk')
            broadcast_message(message, name) # 把接收到的消息转发给其它客户端
    except socket.timeout:
        print(f"【system】地址 {addr} 超时未注册")
    except (ConnectionResetError, BrokenPipeError, OSError):
        print(f'【system】客户端 {name} 已断开连接')
    finally:
        if name: # 已注册客户端从列表移除
            remove_client(conn)
        else: # 未注册客户端直接关闭
            close_conn(conn)

# 心跳消息线程
def heartbeat_thread():
    while True:
        time.sleep(60)
        with clients_lock:
            if not clients:
                continue
        broadcast_message(b'\n', 'server')  # 服务器发送者指定为server

# 处理服务端发送消息的线程
def handle_server_messages():
    while True:
        # command = input("【system】请输入命令或要发送的消息 (输入 'help' 查询指令): ")
        command = input()
        if command.lower() == 'help':
            print("【system】 - exit: 退出")
            print("【system】 - userlist: 查看在线用户列表")
            print("【system】 - disconnect <username>: 断开指定用户连接")
        elif command.lower() == 'exit':
            print("【system】正在关闭所有连接并退出...")
            conns = []
            with clients_lock:
                conns = [client['conn'] for client in clients.values()]
                clients.clear()
            # 关闭所有客户端连接
            for conn in conns:
                close_conn(conn)
            # 关闭主监听套接字
            server_socket.close()
            # 最后退出进程
            os._exit(0)
        elif command.lower() == 'userlist':
            if clients:
                print("【system】当前在线用户列表:")
                for name in clients:
                    print(f"【system】 - {name}")
            else:
                print("【system】 - 没有在线用户")
        elif command.lower().startswith('disconnect '):
            username = command[len('disconnect '):].strip()
            if username in clients:
                client = clients[username]
                remove_client(client['conn'])
                # print(f'【system】用户 {username} 已断开')
            else:
                print(f'【system】找不到用户 {username}')
        elif command:
            broadcast_message(f'【server】{command}\n'.encode('gbk'), 'server') # 按键精灵接收gbk编码

def start_server():
    HOST = '0.0.0.0'
    PORT = 9527

    global server_socket
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind((HOST, PORT))
    server_socket.listen()
    print(f'【system】监听服务已开启，端口：{PORT}', flush=True)

    # 启动心跳线程
    threading.Thread(target=heartbeat_thread, daemon=True).start()

    # 启动处理服务端消息的线程
    threading.Thread(target=handle_server_messages, daemon=True).start()

    while True:
        conn, addr = server_socket.accept()
        # 接受客户端消息的线程（包括连接注册和自定义消息）
        threading.Thread(target=handle_client, args=(conn, addr)).start()

if __name__ == '__main__':
    start_server()
